package engine.gui.elements;

import engine.gui.Component;
import engine.gui.Container;
import engine.gui.GUIStyle;
import engine.gui.Scissor;

public class ScrollPane extends Container{
	public static final int ALIGN_NONE = 0;
	public static final int ALIGN_TOP = 1;
	public static final int ALIGN_LEFT = 1;
	public static final int ALIGN_BOTTOM = 2;
	public static final int ALIGN_RIGHT = 2;
	public static final int ALIGN_CENTER = 3;
	
	private int align; 			//horizontal alignment of the panel
	private int valign;			//vertical alignment of the panel 
	
	//variables, to store, where the panel has been painted
	private int drawX;			
	private int drawY;
	
	//variables, to store the scrolling position of the scrollpane
	private float scrollX;
	private float scrollY;
	
	//variables to store mouse dragging
	private boolean dragX;
	private boolean dragY;
	
	//variables, which store the maximum bounds
	private int maxX;
	private int maxY;
	
	//store the last mouse position
	private int[] lastMousePos = new int[]{-1, -1};
	
	//states, if the horizontal or vertical scrollbar is hovered
	private boolean hoverVertical;
	private boolean hoverHorizontal;
	
	private Scissor scissor;
	
	public ScrollPane() {
		scissor = new Scissor();
		setSolid(true);
	}
	
	@Override
	public void addComponent(Component component) {
		super.addComponent(component);
		
		updateMaxSize();
	}
	
	@Override
	public void removeComponent(Component component) {
		super.removeComponent(component);
		updateMaxSize();
	}
	
	private void updateMaxSize() {
		maxX = 0;
		maxY = 0;
		
		for(Component c: getComponents()) {
			maxX = Math.max(maxX, c.getPosX() + c.getWidth());
			maxY = Math.max(maxY, c.getPosY() + c.getHeight());
		}
	}
	
	public ScrollPane(int posX, int posY, int width, int height) {
		this();
		setPosX(posX);
		setPosY(posY);
		setWidth(width);
		setHeight(height);
	}
	
	@Override
	public void render(int x, int y, int width, int height) {
		GUIStyle style = getCurrentStyle();
		
		switch(align) {
		case ALIGN_LEFT: drawX = getPosX(); break;
		case ALIGN_CENTER: drawX = (width - getWidth() - style.getScrollPaneScrollBarSize()) / 2; break;
		case ALIGN_RIGHT: drawX = width - getWidth() - getPosX() - style.getScrollPaneScrollBarSize(); break;
		default: drawX = getPosX(); break;
		}
		
		switch(valign) {
		case ALIGN_TOP: drawY = getPosY(); break;
		case ALIGN_CENTER: drawY = (height - getHeight() - style.getScrollPaneScrollBarSize()) / 2; break;
		case ALIGN_BOTTOM: drawY = height - getHeight() - getPosY() - style.getScrollPaneScrollBarSize(); break;
		default: drawY = getPosY(); break;
		}
		
		renderSlider(x, y, width, height, style);
		
		int b = (style.getScrollPaneBackgroundImage() != null) ? style.getScrollPaneBackgroundImage().getBorder() : 0;
		scissor.enable(x + getPosX() + b, getWindowManager().getHeight() - (y + getPosY() + getHeight()) + b, getWidth() - 2 * b, getHeight() - 2 * b);
		
		if(style.getScrollPaneBackgroundImage() != null) {
			getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneBackgroundColor());
			
			style.getScrollPaneBackgroundImage().draw(x + drawX - (int)scrollX, y + drawY - (int)scrollY, Math.max(getWidth(), maxX), Math.max(getHeight(), maxY));
		}
		
		super.render(x + drawX - getPosX() - (int)scrollX, y + drawY - getPosY() - (int)scrollY, width, height);
		
		scissor.disable();
		
		if(style.getScrollPaneForegroundImage() != null) {
			getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneForegroundColor());
			
			style.getScrollPaneForegroundImage().draw(x + drawX, y + drawY, getWidth(), getHeight());
		}
	}
	
	private void renderSlider(int x, int y, int width, int height, GUIStyle style) {
		if(style.getScrollPaneSliderBackgroundImage() != null) {
			getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderBackgroundColor());
			
			//render background of vertical scrollbar
			style.getScrollPaneSliderBackgroundImage().draw(x + drawX + getWidth(), y + drawY, style.getScrollPaneScrollBarSize(), getHeight());
			//render background of horizontal scrollbar
			style.getScrollPaneSliderBackgroundImage().draw(x + drawX, y + drawY + getHeight(), getWidth(), style.getScrollPaneScrollBarSize());
		}
		
		//check, if the horizontal slider needs to be rendered
		if(maxX > getWidth()) {
			//check, whether this is hovered or not
			if(hoverHorizontal) {
				if(style.getScrollPaneSliderBackgroundHoverImage() != null) {
					getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderBackgroundHoverColor());
					
					//render background of horizontal scrollbar
					style.getScrollPaneSliderBackgroundHoverImage().draw(x + drawX, y + drawY + getHeight(), getWidth(), style.getScrollPaneScrollBarSize());
				}
			} else {
				if(style.getScrollPaneSliderBackgroundImage() != null) {
					getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderBackgroundColor());
					
					//render background of horizontal scrollbar
					style.getScrollPaneSliderBackgroundImage().draw(x + drawX, y + drawY + getHeight(), getWidth(), style.getScrollPaneScrollBarSize());
				}
			}
			
			if(style.getScrollPaneSliderImage() != null) {
				getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderColor());
				
				//render horizonal slider
				int px = x + drawX + (int)(getPercentageX() * (getHeight() - style.getScrollPaneScrollBarSize()));
				int py = y + drawY + getHeight();
				style.getScrollPaneSliderImage().draw(px, py, style.getScrollPaneScrollBarSize(), style.getScrollPaneScrollBarSize());
			}
		}
		
		//check, if the vertical slider needs to be rendered
		if(maxY > getHeight()) {
			//check, whether this is hovered or not
			if(hoverVertical) {
				if(style.getScrollPaneSliderBackgroundHoverImage() != null) {
					getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderBackgroundHoverColor());
					
					//render background of vertical scrollbar
					style.getScrollPaneSliderBackgroundHoverImage().draw(x + drawX + getWidth(), y + drawY, style.getScrollPaneScrollBarSize(), getHeight());
				}
			} else {
				if(style.getScrollPaneSliderBackgroundImage() != null) {
					getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderBackgroundColor());
					
					//render background of vertical scrollbar
					style.getScrollPaneSliderBackgroundImage().draw(x + drawX + getWidth(), y + drawY, style.getScrollPaneScrollBarSize(), getHeight());
				}
			}
			
			if(style.getScrollPaneSliderImage() != null && maxY > getHeight()) {
				getWindowManager().getGuiShader().sendUniform4f("color", style.getScrollPaneSliderColor());
				
				//render vertical slider
				int px = x + drawX + getWidth();
				int py = y + drawY + (int)(getPercentageY() * (getHeight() - style.getScrollPaneScrollBarSize()));
				style.getScrollPaneSliderImage().draw(px, py, style.getScrollPaneScrollBarSize(), style.getScrollPaneScrollBarSize());
			}
		}
	}
	
	private float getPercentageX() {
		return scrollX / (maxX - getWidth());
	}
	
	private float getPercentageY() {
		return scrollY / (maxY - getHeight());
	}
	
	public int getAlign() {
		return align;
	}
	
	public void setAlign(int align) {
		this.align = align;
	}
	
	public void setValign(int valign) {
		this.valign = valign;
	}
	
	public int getValign() {
		return valign;
	}
	
	@Override
	public boolean mousePressed(int posX, int posY, int button) {
		GUIStyle style = getCurrentStyle();
		
		//check, if horizontal scrolling is enabled
		if(button == 0 && maxX > getWidth()) {
			//check, if the mouse intersects with the whole scrollbar
			int x = getPosX();
			int y = getPosY();
			int b = style.getScrollPaneScrollBarSize();
			
			if(posX > x && posX < x + getWidth() && posY > y + getHeight() && posY < y + getHeight() + b) {
				//the target position of the slider will be centered according to the slider
				setScrollPosition((float)(posX - x - b / 2) / (getWidth() - b) * (maxX - getWidth()), scrollY);
				
				dragX = true;
				return true;
			}
		}
		
		if(button == 0 && maxY > getHeight()) {
			//check, if the mouse intersects with the scrollbar
			int x = getPosX();
			int y = getPosY();
			int b = style.getScrollPaneScrollBarSize();
			
			if(posX > x + getWidth() && posX < x + getWidth() +  b && posY > y && posY < y + getHeight()) {
				//the target position of the slider will be centered according to the slider
				setScrollPosition(scrollX, (float)(posY - y - b / 2) / (getHeight() - b) * (maxY - getHeight()));
				
				dragY = true;
				return true;
			}
		}
		
		return mousePressedContainer(posX, posY, button);
	}
	
	@Override
	public boolean mouseReleased(int posX, int posY, int button) {
		dragX = false;
		dragY = false;
		
		return mouseReleasedContainer(posX, posY, button);
	}
	
	@Override
	public boolean mouseMoved(int posX, int posY, int button) {
		lastMousePos[0] = posX;
		lastMousePos[1] = posY;
		
		hoverVertical = false;
		hoverHorizontal = false;
		
		if(maxX > getWidth()) {
			//check, if the mouse intersects with the whole scrollbar
			int x = getPosX();
			int y = getPosY();
			int b = getCurrentStyle().getScrollPaneScrollBarSize();
			
			hoverHorizontal = posX > x && posX < x + getWidth() && posY > y + getHeight() && posY < y + getHeight() + b;
		}
		
		if(maxY > getHeight()) {
			//check, if the mouse intersects with the scrollbar
			int x = getPosX();
			int y = getPosY();
			int b = getCurrentStyle().getScrollPaneScrollBarSize();
			
			hoverVertical = posX > x + getWidth() && posX < x + getWidth() +  b && posY > y && posY < y + getHeight();
		}
		
		return mouseMovedContainer(posX, posY, button);
	}
	
	@Override
	public boolean mouseDragged(int posX, int posY, int button) {
		if(dragX) {
			int x = getPosX();
			int b = getCurrentStyle().getScrollPaneScrollBarSize();
			setScrollPosition((float)(posX - x - b / 2) / (getWidth() - b) * (maxX - getWidth()), scrollY);
		}
		
		if(dragY) {
			int y = getPosY();
			int b = getCurrentStyle().getScrollPaneScrollBarSize();
			setScrollPosition(scrollX, (float)(posY - y - b / 2) / (getHeight() - b) * (maxY - getHeight()));
		}
		
		lastMousePos[0] = posX;
		lastMousePos[1] = posY;
		
		return mouseDraggedContainer(posX, posY, button);
	}
	
	@Override
	public boolean mouseWheelMoved(int movement) {
		int s = getCurrentStyle().getScrollPaneScrollBarSize();
		
		if(lastMousePos[0] > drawX && lastMousePos[0] < drawX + getWidth() + s && lastMousePos[1] > drawY && lastMousePos[1] < drawY + getHeight() + s) {
			setScrollPosition(scrollX, scrollY - 6 * movement);
			return false;
		}
		
		return super.mouseWheelMoved(movement);
	}
	
	private void setScrollPosition(float scrollX, float scrollY) {
		this.scrollX = Math.max(0, Math.min(maxX - getWidth(), scrollX));
		this.scrollY = Math.max(0, Math.min(maxY - getHeight(), scrollY));
	}
	
	//omit changing solid value
	@Override
	public void setSolid(boolean solid) {
	}
	
	public boolean mousePressedContainer(int posX, int posY, int button) {
		if (posX > getPosX() && posX < getPosX() + getWidth()
				&& posY > getPosY() && posY < getPosY() + getHeight()) {
			int sx = (int)scrollX;
			int sy = (int)scrollY;
			
			for (Component c : getComponents()) {
				if (c.mousePressed(posX - getPosX() +  sx, posY - getPosY() + sy, button)) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean mouseReleasedContainer(int posX, int posY, int button) {
		if (posX > getPosX() && posX < getPosX() + getWidth()
				&& posY > getPosY() && posY < getPosY() + getHeight()) {
			int sx = (int)scrollX;
			int sy = (int)scrollY;
			
			for (Component c : getComponents()) {
				if (c.mouseReleased(posX - getPosX() +  sx, posY - getPosY() + sy, button)) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean mouseMovedContainer(int posX, int posY, int button) {
		if (posX > getPosX() && posX < getPosX() + getWidth()
				&& posY > getPosY() && posY < getPosY() + getHeight()) {
			int sx = (int)scrollX;
			int sy = (int)scrollY;
			
			for (Component c : getComponents()) {
				if (c.mouseMoved(posX - getPosX() +  sx, posY - getPosY() + sy, button)) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean mouseDraggedContainer(int posX, int posY, int button) {
		if (posX > getPosX() && posX < getPosX() + getWidth()
				&& posY > getPosY() && posY < getPosY() + getHeight()) {
			int sx = (int)scrollX;
			int sy = (int)scrollY;
			
			for (Component c : getComponents()) {
				if (c.mouseDragged(posX - getPosX() +  sx, posY - getPosY() + sy, button)) {
					return true;
				}
			}
		}
		return false;
	}
}
